package logs;

import java.io.*;
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;

/**
 * Trida urcena pro logovani udalosti.
 * 
 * @author Petr Altman
 */
public class Logger {
	
	private static PrintWriter out = null;
	private static boolean verbose = false;
	private static File f = null;
	private static OutputStream fos = null;
	
	private static boolean debug = true, info = true, error = true;
	
	/**
	 * Vrati stav, zda se maji vypisovat ladici zaznamy.
	 * @return Stav vypisovani ladicich zaznamu.
	 */
	public static boolean isDebug() {
		return debug;
	}

	/**
	 * Nastavi stav vypisovani ladicich zaznamu.
	 * @param debug Novy stav vypisovani ladicich zaznamu.
	 */
	public static void setDebug(boolean debug) {
		Logger.debug = debug;
	}

	/**
	 * Vrati stav, zda se maji vypisovat informacni zaznamy.
	 * @return Stav vypisovani informacnich zaznamu.
	 */
	public static boolean isInfo() {
		return info;
	}

	/**
	 * Nastavi stav vypisovani informacnich zaznamu.
	 * @param info Novy stav vypisovani informacnich zaznamu.
	 */
	public static void setInfo(boolean info) {
		Logger.info = info;
	}

	/**
	 * Vrati stav vypisovani chybovych zaznamu.
	 * @return Stav vypisovani chybovych zaznamu.
	 */
	public static boolean isError() {
		return error;
	}

	/**
	 * Nastavi stav vypisovani chybovych zaznamu.
	 * @param error Novy stav vypisovani chybovych zaznamu.
	 */
	public static void setError(boolean error) {
		Logger.error = error;
	}

	/**
	 * Vrati stav, zda budou zaznamy vypisovany do konzole.
	 * @return Stav zda budou zaznamy vypisovany do konzole.
	 */
	public static boolean getVerbose() {
		return verbose;
	}
	
	/**
	 * Nastavi stav vypisovani zaznamu do konzole.
	 * @param verbose Novy stav vypisovani zaznamu do konzole.
	 */
	public static void setVerbose(Boolean verbose) {
		Logger.verbose = verbose;
	}
	
	private static LoggerListener listener;
	/**
	 * Vrati listener loggeru.
	 * @return Listener loggeru.
	 */
	public static LoggerListener getListener() {
		return listener;
	}

	/**
	 * Nastavi dany objekt implementujici rozhrani <code>LoggerListener</code> jako listener loggeru.
	 * @param listener Objekt implementujici rozhrani <code>LoggerListener</code>.
	 */
	public static void setListener(LoggerListener listener) {
		Logger.listener = listener;
	}

	static {
		f = new File(getCurrentDirectory() + "/logs/log_" + getDateTime("dd_MM_yyyy_HH_mm_ss") + ".log");
		
		// Inform user that there is still something happening...
		System.out.print(String.format("Log file: %s", f.getName()));
		
		// Create new log in the given file
		try {
			createNewLog(f);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		// Report big DONE sign, user should know, that this program works :))
		System.out.println("...ok");
	}
	
	/**
	 * Validates the file and creates instance of PrintWriter for work with the log
	 * @param f Given file
	 * @throws IOException Typically occurs when the file is already opened, but covers all possible exceptions on the file.
	 */
	public static void createNewLog(File f) throws IOException {
		// If the another file with the same name already exists, delete it.
		if(f.exists() && !f.delete()) 
 			throw new IOException("Log file already exists and cannot be deleted, maybe another program instance is also running?");
			
		// Create logfile		
		if(!f.createNewFile())
			throw new IOException("Log file cannot be created for unkown reason.");
		
		// There we have logfile created for ourselves, ready to be used, let's do it:
		fos = new FileOutputStream(f);
		out = new PrintWriter(fos);

	}
	
	/**
	 * Adds entry into the log.
	 * @param entry LogEntryInfo instance, messenger with information about log entry
	 */
	public static synchronized void addLogEntry(LogEntryInfo entry) {
		addLogEntry(entry.getType(), String.format("%s, %s, %s", 
				getDateTime("dd.MM.yyyy, HH:mm:ss"), entry.getMessage(), entry.getLabel()), false);
	
	}
	
	/**
	 * Print log into the file and if verbose mode is set, also into the system.out stream 
	 * @param text Text of the log entry to single line
	 */
	public static synchronized void addLogEntry(LogEntryTypes type, String text, boolean forceVerbose){
		String str = type.toString() + "; " + text;
		
		// Take care about the file
		out.println(str);
		out.flush();
				
		// Take care about the verbose mode
		if(verbose || forceVerbose) { 
			System.out.println(str);
			
			if(listener != null)
				listener.onLogEntryAdded(str);
		}
		
	}
	
	/**
	 * Adds a new info entry to the log.
	 * @param text Entry message.
	 * @param forceVerbose Whether the entry should be printed to the console.
	 */
	public static synchronized void addInfo(String text, boolean forceVerbose) {
		addLogEntry(LogEntryTypes.INFO, text, forceVerbose && isInfo());
	}
	
	/**
	 * Adds a new error entry to the log.
	 * @param text Entry message.
	 * @param forceVerbose Whether the entry should be printed to the console.
	 */
	public static synchronized void addError(String text, boolean forceVerbose) {
		addLogEntry(LogEntryTypes.ERROR, text, forceVerbose && isError());
	}
	
	/**
	 * Adds a new debug entry to the log.
	 * @param text Entry message.
	 * @param forceVerbose Whether the entry should be printed to the console.
	 */
	public static synchronized void addDebug(String text, boolean forceVerbose) {
		addLogEntry(LogEntryTypes.DEBUG, text, forceVerbose && isDebug());
	}
	
    /**
	 * Simply returns actual date & time
	 * @return Actual date and time in the typical format
	 */
	private static String getDateTime(String format) {
        	DateFormat dateFormat = new SimpleDateFormat(format);
        	Date date = new Date();
		return dateFormat.format(date);
	}
	
	/**
	 * Return a current directory where the process is running. 
	 * @return Directory of the current process.
	 */
	public static String getCurrentDirectory() {
		String dir = "";
		try {
			dir = (new File(".")).getCanonicalPath();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return dir;
	}
}

